1. Field of the Invention
The invention relates to in general to dynamic analysis of computer software targeted to a virtual machine, and more particularly, to instrumenting program code executed using a virtual machine.
2. Description of the Related Art
A typical computing system comprises a hardware layer, an operating system layer and an application layer that runs on top of the operating system. Virtualization software abstracts a virtual machine (VM) by interposing an additional layer within the system. A virtual machine itself is a target for a programmer or compilation system. A VM runs programs written and compiled for its abstract machine definition.
An application layer VM, for example, sits as an application program on top of an operating system. A typical application level VM runs as a normal application inside an operating system. The VM can be created when a process is started and can be destroyed when the process exits. A VM abstracts away details of the underlying hardware and operating system to allow a program targeted to the VM to execute in the same way on different computer system platforms. Java, Smalltalk and the .NET Framework are examples of application layer VM environments. A Java virtual machine (JVM) is a virtual machine that executes object oriented code known as bytecode that is compiled specifically to it. The Java programming language does not rely on platform-specific instructions sets, such as APIs specific to any one operating system to access or display resources such as files. Rather, in a Java runtime environment, Java-specific commands are interpreted.
FIGS. 1A-1B are illustrative drawings of a compile-time environment (FIG. 1A) and a run-time environment (FIG. 1B) in a VM environment. In particular, the example in FIGS. 1A-1B illustrates the operation of a JVM. FIG. 1A shows that a Java compiler 102 compiles user program source files 104 written in the Java programming language to class files 106 on the JVM, which comprise the program code executed by the machine. In a Java program, for example, system resources are accessed by calling methods in classes that that comprise program code to implement the Java Application Programming Interface (API). FIG. 1B shows that a JVM 108 runs a program comprising user program class files 106, it fulfills user program calls by user application program 106 to the Java API by invoking methods from the class files 110 that implement the Java API.
FIG. 2 is an illustrative drawing of an application layer VM 108 that includes a class loader subsystem 112 and an execution engine 114. The illustrated application level VM runs on top of an operating system layer 116 and executes object oriented program code. It loads class files and executes the code contained within those files. More specifically, for example, a JVM, loads class files and executes the bytecodes that they contain. A JVM class loader 112 loads class files from both a user program (e.g., user-defined program code classes) and the Java API (e.g., bootstrap classes). In general, only those class files that are actually required by a running program are loaded into the virtual machine. This is sometimes referred to as ‘lazy’ execution in that JAVA class files may be loaded on an as needed basis. In a Java environment, for example, execution can take different forms. For instance, the execution engine 114 can be implemented to interpret the bytecodes of a method in a program one at a time. Alternatively, for example, the execution engine 114 can be implemented to employ a just-in-time compiler in which bytecodes of a method are compiled to native machine code the first time the method is invoked. The native machine code for the method is cached so it can be re-used later when that same method is invoked again.
A JVM class loader subsystem 112 typically includes two kinds of class loaders: a bootstrap class loader and user-defined class loaders. The bootstrap class loader is a part of the virtual machine implementation. User-defined class loaders are part of the user-program written in the Java language that runs on the JVM. Classes loaded by different class loaders are placed into separate name spaces inside the Java virtual machine. The JVM class loader subsystem 112 handles more than just locating and importing the binary data for classes. It also verifies the correctness of imported classes, allocates and initializes memory for class variables, and assists in the resolution of symbolic references, for example. The JVM implementation employs a bootstrap class loader, which knows how to load trusted classes, including the classes of the Java API, so called ‘bootstrap classes’. Java employs a linking model to enable a designer to develop user-defined class loaders that extend a Java application in custom ways at run-time. Through user-defined class loaders, a Java application can load and dynamically link to classes and interfaces that were unknown or did not even exist when the application was compiled.
An instrumentation routine may be used to identify, locate, and modify specific computer program components to enable monitoring, analysis or debugging, for example. Such an instrumentation process is sometimes characterized as “instrumenting the code.” In a Java environment, instrumentation typically involves the addition of instrumentation code (i.e. bytecode) to program code of methods defined in classes for the purpose of gathering data to be utilized by dynamic analysis tools, used for optimizing, characterizing, benchmarking, debugging or otherwise improving the robustness of a software program as it executes, for example. The additional instrumentation code generally does not fundamentally change the state or behavior of the application modified through the addition of such instrumentation code. Examples of such analysis tools include monitoring agents, profilers, coverage analyzers, event loggers and debuggers, for example.
One approach to instrumentation in a Java environment involves use of a Java ‘Instrumentation’ class and special Java libraries called Java agents. A Java agent typically utilizes a pluggable library that runs embedded in a JVM and that intercepts the classloading process. This allows a software agent to monitor the classloading process and to instrument the bytecode of the classes to provide informational callbacks into the agent's libraries, for example. Thus, one approach to the instrumentation of an object-oriented application targeted to a VM is to modify classes through the addition of instrumentation code (e.g., bytecode) as the classes are loaded.
However, in a JVM environment, these agents typically are loaded only after the JVM has embarked upon bootstrapping of bootstrap classes. At that stage of execution, several hundred bootstrap classes already may have been loaded but not yet have been processed by any instrumentation agent. Thus, these bootstrap classes will not have been instrumented by the instrumentor. One proposed solution to the problem is to statically instrument the bootstrap classes, which involves instrumenting them ahead of time and causing the JVM to use the instrumented classes when it starts up. However, this solution can be problematic since it involves modifying where the JVM loads bootstrap classes, which can lead to ease of use issues. See, Walter Binder, Jade Hulaas and Philippe Moret, Reengineering Standard Java Runtime Systems through Dynamic Bytecode Instrumentation, Seventh IEEE Working Conference on Source Code Analysis and Manipulation, SCAM 2007, Volume, Issue, Sep. 30-Oct. 1, 2007, pages, 91-100. Moreover, classes of the computer software based instrumentation process itself, i.e. its instrumentation classes, may not be instrumented either.
Thus, there is a need for improvement of techniques to instrument of computer program applications targeted to VMs that employ bootstrap classes. The present invention meets this need.